#! /usr/bin/env python
# generate config file using sdk.S
# this script is primarily used to make sure that the game
# imports all needed functions and make hooks whenever possible

# clear the config file and open for appending
config = open("config.h", "w")
config.close()
config = open("config.h", "a")

# open sdk.S file
sdk = open("sdk.S", "r")

stubs = sdk.readlines()

hooks = []

def find_address(i):
	"""Parse an sdk.S line to get the address of a function
	not needed anymore but kept for historical purposes"""
	j = 0
	char = i[j]
	found = False
	while not found:
		if j == len(i): break
		j+=1
		char = i[j]
		if char == ",": found = True
	if found:
		j+=2
		return (i[j:][:10])
	else: return "TODO"

def parse_param_sfo():
	"Parse a PARAM.SFO to find the savedata's name"
	try: param = open("PARAM.SFO", "r")
	except: return "TODO"
	else:
		param.seek(0x510)
		char = param.read(1)
		name = ""
		while True:
			if char.encode("hex") == "00": break
			name += char
			char = param.read(1)
		if name == "": return "TODO"
		else: return name

def make_hooks():
	"Check that the game imports all needed functions, make necessary hooks if needed"
	global hooks

	sceIoOpen = False
	sceIoRead = False
	sceIoWrite = False
	sceIoCLose = False
	sceIoOpenAsync = False
	sceIoReadAsync = False
	sceIoWriteAsync = False
	sceIoCLoseAsync = False
	sceDisplayWaitVblankStart = False
	sceKernelDelayThread = False
	sceDisplaySetFrameBuf = False
	sceDisplaySetMode = False
	sceCtrlSetSamplingMode = False
	sceKernelExitGame = False
	sceCtrlPeekBufferPositive = False
	sceCtrlReadBufferPositive = False
	sceKernelDcacheWritebackAll = False
	sceKernelDcacheWritebackInvalidateAll = False
	sceKernelDcacheInvalidateRange = False
	

	for i in stubs:
		if "sceIoOpen," in i:
			sceIoOpen = True
		elif "sceIoRead," in i:
			sceIoRead = True
		elif "sceIoWrite," in i:
			sceIoWrite = True
		elif "sceIoClose," in i:
			sceIoClose = True
		elif "sceIoOpenAsync" in i:
			sceIoOpenAsync = True
		elif "sceIoReadAsync" in i:
			sceIoReadAsync = True
		elif "sceIoWriteAsync" in i:
			sceIoWriteAsync = True
		elif "sceIoCloseAsync" in i:
			sceIoCloseAsync = True
		elif "sceDisplayWaitVblankStart" in i:
			sceDisplayWaitVblankStart = True
		elif "sceKernelDelayThread" in i:
			sceKernelDelayThread = True
		elif "sceDisplaySetFrameBuf" in i:
			sceDisplaySetFrameBuf = True
		elif "sceDisplaySetMode" in i:
			sceDisplaySetMode = True
		elif "sceCtrlSetSamplingMode" in i:
			sceCtrlSetSamplingMode = True
		elif "sceKernelExitGame" in i:
			sceKernelExitGame = True
		elif "sceCtrlPeekBufferPositive" in i:
			sceCtrlPeekBufferPositive = True
		elif "sceCtrlReadBufferPositive" in i:
			sceCtrlReadBufferPositive = True
		elif "sceKernelDcacheWritebackAll" in i:
			sceKernelDcacheWritebackAll = True
		elif "sceKernelDcacheWritebackInvalidateAll" in i:
			sceKernelDcacheWritebackInvalidateAll = True
		elif "sceKernelDcacheInvalidateRange" in i:
			sceKernelDcacheInvalidateRange = True

	# known hooks
	if not sceDisplayWaitVblankStart:
		if sceKernelDelayThread:
			print "hooking sceDisplayWaitVblankStart with sceKernelDelayThread"
			hooks += ["\n#define sceDisplayWaitVblankStart() sceKernelDelayThread(100)"]
		else:
			hooks += ["\n#define sceDisplayWaitVblankStart() Sleep(100)"]
			print """WARNING! your game does not import sceDisplayWaitVblankStart nor sceKernelDelayThread
              you might not be able to port multiBootCFW to this game"""

	if not sceCtrlReadBufferPositive:
		if sceCtrlPeekBufferPositive:
			print "hooking sceCtrlReadBufferPositive with sceCtrlPeekBufferPositive"
			hooks += ["\n#define sceCtrlReadBufferPositive sceCtrlPeekBufferPositive"]
		else: print """WARNING! your game does not import sceCtrlReadBufferPositive/sceCtrlPeekBufferPositive
              you might not be able to port multiBootCFW to this game"""

	if not sceKernelDcacheWritebackAll:
		if sceKernelDcacheWritebackInvalidateAll:
			print "hooking sceKernelDcacheWritebackAll with sceKernelDcacheWritebackInvalidateAll"
			hooks += ["\n#define sceKernelDcacheWritebackAll sceKernelDcacheWritebackInvalidateAll"]
		elif sceKernelDcacheInvalidateRange:
			print "hooking sceKernelDcacheWritebackAll with sceKernelDcacheInvalidateRange"
			hooks += ["\n#define sceKernelDcacheWritebackAll() sceKernelDcacheInvalidateRange(0x08D20000, 0x10000000)"]
		else:
			print """ERROR! your game does not import sceKernelDcacheWritebackAll nor a similar function
              you will not be able to port multiBootCFW to this game"""
			raise SystemExit("")

	if not sceIoOpen:
		if sceIoOpenAsync:
			print "hooking sceIoOpen with sceIoOpenAsync"
			hooks += ["\n#define sceIoOpen sceIoOpenAsync"]
		else:
			print """ERROR! your game does not import sceIoOpen and no hook has been defined
              you will not be able to port multiBootCFW to this game"""
			raise SystemExit("")

	if not sceIoRead:
		if sceIoReadAsync:
			print "hooking sceIoRead with sceIoReadAsync"
			hooks += ["\n#define sceIoRead sceIoReadAsync"]
		else:
			print """ERROR! your game does not import sceIoRead and no hook has been defined
              you will not be able to port multiBootCFW to this game"""
			raise SystemExit("")

	if not sceIoClose:
		if sceIoCloseAsync:
			print "hooking sceIoClose with sceIoCloseAsync"
			hooks += ["\n#define sceIoClose sceIoCloseAsync"]
		else:
			print """ERROR! your game does not import sceIoClose and no hook has been defined
              you will not be able to port multiBootCFW to this game"""
			raise SystemExit("")

	# dummy hooks
	if not sceIoWrite:
		if sceIoWriteAsync:
			print "hooking sceIoWrite with sceIoWriteAsync"
			hooks += ["\n#define sceIoWrite sceIoWriteAsync"]
		else:
			hooks += ["\nint sceIoWrite (SceUID fd, const void *data, SceSize size){return 0;}"]
			print """WARNING! your game does not import sceIoWrite and a dummy hook has been defined
              you will not be able to port multiBootCFW to this game"""

	if not sceCtrlSetSamplingMode:
		hooks += ["\nint sceCtrlSetSamplingMode (int mode){return mode;}"]
		print """WARNING! your game does not import sceCtrlSetSamplingMode and a dummy hook has been defined
              you might not be able to port multiBootCFW to this game"""

	# unknown hooks
	if not sceDisplaySetFrameBuf:
		print """ERROR! your game does not import sceDisplaySetFrameBuf and no hook has been defined
              you will not be able to port multiBootCFW to this game"""
		raise SystemExit("")
	if not sceDisplaySetMode:
		print """ERROR! your game does not import sceDisplaySetMode and no hook has been defined
              you will not be able to port multiBootCFW to this game"""
		raise SystemExit("")
	if not sceKernelExitGame:
		print """ERROR! your game does not import sceKernelExitGame and no hook has been defined
              you will not be able to port multiBootCFW to this game"""
		raise SystemExit("")

# check that all functions are imported, hook if needed
make_hooks()

# parse PARAM.SFO for the savedata's name
savename = parse_param_sfo()
if savename == "TODO": print "Unable to find folder name, you have to manually edit config.h"
else: print "Found folder name:", savename

# write config file
config.write("""#include "sdk.h"

#ifndef CONFIG_H
#define CONFIG_H

""")
config.write("\n// Function hooks")
for i in hooks: config.write(i)
config.write("\n//")
config.write("\n")
config.write("""
#define MBCFW_ROOT "ms0:/PSP/SAVEDATA/"""+savename+'/"\n')
config.write(
"""#define TNV_BIN MBCFW_ROOT"TV.BIN" // TN-V's TN.BIN renamed to TV.BIN
//#define TNC_BIN MBCFW_ROOT"TC.BIN" // TN-C's TN.BIN renamed to TC.BIN
#define ARK_BIN MBCFW_ROOT"CB.BIN" // ARK's H.BIN renamed to CB.BIN
#define HBL_BIN MBCFW_ROOT"WL.BIN" // HBL's H.BIN renamed to WL.BIN

#endif""")
config.close()
print "\nAll done!"
